# --coding:utf-8--

# Copyright 2021 xpstem.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from lelink.ev3type import *


class LCEncoder:
    __slots__ = ("_int_val", "_str_val")

    # long format, 1 byte follow
    LC_BYTE1_PREFIX = 0b10000001

    # long format, 2 bytes follow
    LC_BYTE2_PREFIX = 0b10000010

    # long format, 4 byte follow
    LC_BYTE4_PREFIX = 0b10000011

    # zero terminated string
    LC_STR_PREFIX = 0b10000100

    def __init__(self, obj):
        if isinstance(obj, int):
            self._int_val = obj
        elif isinstance(obj, str):
            self._str_val = obj
        elif isinstance(obj, set):
            value = 0
            for port in obj:
                value = value + port.value
            self._int_val = value
        elif isinstance(obj, InputPort):
            self._int_val = obj.value
        elif isinstance(obj, Layer):
            self._int_val = obj.value
        elif isinstance(obj, EV3Type):
            self._int_val = obj.value


    def lc0(self, ba: bytearray) -> None:
        if self._int_val >= 0:
            enc_val = self._int_val & 0b00011111
        else:
            enc_val = (0 - self._int_val) & 0b00111111
        ba.append(enc_val)

    def _lc1(self, ba: bytearray) -> None:
        ba.append(LCEncoder.LC_BYTE1_PREFIX)
        ba.append(self._int_val & 0x000000ff)

    def _lc2(self, ba: bytearray) -> None:
        ba.append(LCEncoder.LC_BYTE2_PREFIX)
        ba.append(self._int_val & 0x000000ff)
        ba.append((self._int_val >> 8) & 0x000000ff)

    def _lc4(self, ba: bytearray) -> None:
        ba.append(LCEncoder.LC_BYTE4_PREFIX)
        ba.append(self._int_val & 0x000000ff)
        ba.append((self._int_val >> 8) & 0x000000ff)
        ba.append((self._int_val >> 16) & 0x000000ff)
        ba.append((self._int_val >> 24) & 0x000000ff)

    def lcx(self, ba: bytearray) -> None:
        if -32 < self._int_val < 32:
            self.lc0(ba)
        elif -128 < self._int_val < 128:
            self._lc1(ba)
        elif -32768 < self._int_val < 32768:
            self._lc2(ba)
        else:
            self._lc4(ba)

    def lcs(self, ba: bytearray) -> None:
        ba.append(LCEncoder.LC_STR_PREFIX)
        ba.extend(self._str_val.encode('utf-8'))
        ba.append(0x00)


class GVEncoder:

    # short format
    GV_SHORT_PREFIX = 0b01100000

    # long format, 1 byte follow
    GV_BYTE1_PREFIX = 0b11100001

    # long format, 2 bytes follow
    GV_BYTE2_PREFIX = 0b11100010

    # long format, 4 bytes follow
    GV_BYTE4_PREFIX = 0b11100011

    # zero terminated string
    GV_STR_PREFIX = 0b11100100;

    def __init__(self, obj):
        if isinstance(obj, int):
            self._int_val = obj
        elif isinstance(obj, str):
            self._str_val = obj

    def gv0(self, ba: bytearray) -> None:
        enc_val = GVEncoder.GV_SHORT_PREFIX + (self._int_val & 0b00011111)
        ba.append(enc_val)

    def _gv1(self, ba: bytearray) -> None:
        ba.append(GVEncoder.GV_BYTE1_PREFIX)
        ba.append(self._int_val & 0x000000ff)

    def _lc2(self, ba: bytearray) -> None:
        ba.append(GVEncoder.GV_BYTE2_PREFIX)
        ba.append(self._int_val & 0x000000ff)
        ba.append((self._int_val >> 8) & 0x000000ff)

    def _lc4(self, ba: bytearray) -> None:
        ba.append(GVEncoder.GV_BYTE4_PREFIX)
        ba.append(self._int_val & 0x000000ff)
        ba.append((self._int_val >> 8) & 0x000000ff)
        ba.append((self._int_val >> 16) & 0x000000ff)
        ba.append((self._int_val >> 24) & 0x000000ff)

    def gvx(self, ba: bytearray) -> None:
        if -32 < self._int_val < 32:
            self.gv0(ba)
        elif -128 < self._int_val < 128:
            self._gv1(ba)
        elif -32768 < self._int_val < 32768:
            self._gv2(ba)
        else:
            self._gv4(ba)

    def gvs(self, ba: bytearray) -> None:
        ba.append(GVEncoder.GV_STR_PREFIX)
        ba.extend(self._str_val)
        ba.append(0x00)
